Resolve #11: Eliminate code duplication in BBL parser#12
Conversation
- Create src/parser/helpers.rs with 7 sign_extend functions - Export helpers from parser/mod.rs - Remove bbl_format from lib.rs (will be CLI-only module) - Update stream.rs to use shared helpers - Update bbl_format.rs to use shared helpers - All tests pass, CSV output identical to master
- Add debug parameter to crate's parse_frame_data - Add apply_predictor_with_debug with field_names, skipped_frames support - Add vbatLatest corruption prevention logic to crate - Add read_tag8_8svb_counted method with proper group counting - Fix ENCODING_TAG8_8SVB handling to count consecutive fields - Update main.rs to import from bbl_parser crate: * BBLDataStream, parse_frame_data, FrameDefinition * sign_extend_14bit, encoding constants - Remove duplicate FieldDefinition/FrameDefinition from main.rs - Delete bbl_format.rs entirely (509 lines removed) - All CSV outputs verified identical to master branch
📝 WalkthroughWalkthroughThis PR deletes the CLI-local BBL parser file and fully migrates its sign-extension, stream, predictor, and frame-parsing logic into the crate: adds parser helpers, enhances predictor/debugging, updates stream/tag readers, threads a debug flag through frame parsing, and updates CLI uses to the crate's public types and functions. Changes
Estimated code review effort🎯 4 (Complex) | ⏱️ ~60 minutes
Possibly related PRs
Pre-merge checks and finishing touches✅ Passed checks (5 passed)
✨ Finishing touches
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 1
🧹 Nitpick comments (3)
src/parser/frame.rs (1)
443-450: Consider moving the import to module level.The function-scoped
usestatement on line 449 works but is unconventional. Sinceapply_predictor_with_debugis already exported fromdecoder, consider adding it to the existing import on line 1:-use crate::parser::{decoder::*, event::parse_e_frame, gps::*, stream::BBLDataStream}; +use crate::parser::{decoder::*, decoder::apply_predictor_with_debug, event::parse_e_frame, gps::*, stream::BBLDataStream};Or remove line 449 since
decoder::*already includes it via the glob import.src/parser/decoder.rs (2)
100-129: Consider extracting magic number to named constant.The corruption threshold
1000for vbatLatest is used in the detection logic. Consider extracting this to a named constant for self-documentation and consistency if the same threshold is used elsewhere.+// Maximum reasonable raw vbatLatest value before considering it corrupted +const MAX_REASONABLE_VBAT_RAW: i32 = 1000; + PREDICT_PREVIOUS => { if let Some(prev) = previous_frame { if field_index < prev.len() { let result = prev[field_index] + raw_value; // CRITICAL FIX: Prevent corruption propagation for vbatLatest if field_names .get(field_index) .map(|name| name == "vbatLatest") .unwrap_or(false) { // Check if previous value is corrupted (way too high for voltage) - if prev[field_index] > 1000 { + if prev[field_index] > MAX_REASONABLE_VBAT_RAW {
160-176: Hardcoded fallback index may cause silent data corruption.When
field_namesis empty ormotor[0]is not found, the fallback to hardcoded index39could produce incorrect results if the I-frame field layout differs from the expected structure. Consider either:
- Returning just
raw_valuewhen motor[0] cannot be located- Adding debug logging when the fallback is used
// Fallback: use hardcoded position (typically field 39 in I-frame) let motor0_index = 39; if motor0_index < current_frame.len() { + if debug { + eprintln!("DEBUG: PREDICT_MOTOR_0 using hardcoded fallback index {}", motor0_index); + } current_frame[motor0_index] + raw_value } else { raw_value }
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (9)
src/bbl_format.rs(0 hunks)src/lib.rs(0 hunks)src/main.rs(15 hunks)src/parser/decoder.rs(2 hunks)src/parser/frame.rs(6 hunks)src/parser/gps.rs(1 hunks)src/parser/helpers.rs(1 hunks)src/parser/mod.rs(2 hunks)src/parser/stream.rs(3 hunks)
💤 Files with no reviewable changes (2)
- src/lib.rs
- src/bbl_format.rs
🧰 Additional context used
📓 Path-based instructions (3)
**/*.rs
📄 CodeRabbit inference engine (AGENTS.md)
**/*.rs: Do not remove or modify comments unless the related code is changed
Only add comments that explain code functionality; do not add AI instructional comments
Never embed or call external binaries from Rust code
Files:
src/parser/helpers.rssrc/parser/mod.rssrc/main.rssrc/parser/frame.rssrc/parser/gps.rssrc/parser/stream.rssrc/parser/decoder.rs
{src/**/*.rs,Cargo.*,README.md,OVERVIEW.md,.gitignore,.github/**}
📄 CodeRabbit inference engine (AGENTS.md)
Only commit changes to
src/**/*.rs,Cargo.*,README.md,OVERVIEW.md,.gitignore, and.github/**; never usegit add .orgit add -A
Files:
src/parser/helpers.rssrc/parser/mod.rssrc/main.rssrc/parser/frame.rssrc/parser/gps.rssrc/parser/stream.rssrc/parser/decoder.rs
src/main.rs
📄 CodeRabbit inference engine (AGENTS.md)
Maintain the application entry point at
src/main.rs
Files:
src/main.rs
🧠 Learnings (1)
📓 Common learnings
Learnt from: nerdCopter
Repo: nerdCopter/bbl_parser PR: 2
File: LICENSE_COMMERCIAL:1-4
Timestamp: 2025-08-29T19:52:05.099Z
Learning: nerdCopter prefers to avoid publishing personal information in license files for privacy and security reasons, as they are an individual maintainer rather than a company.
Learnt from: nerdCopter
Repo: nerdCopter/bbl_parser PR: 2
File: README.md:520-521
Timestamp: 2025-08-29T19:53:41.354Z
Learning: nerdCopter uses AGPL-3.0-or-later licensing for the bbl_parser project with a dual-licensing approach that includes a separate commercial license option.
Learnt from: nerdCopter
Repo: nerdCopter/bbl_parser PR: 2
File: CONTRIBUTING.md:9-14
Timestamp: 2025-08-21T20:25:45.741Z
Learning: nerdCopter prefers to keep CLA language general using "project maintainer" rather than specifying a legal entity name, as they are an individual maintainer without an associated company.
Learnt from: nerdCopter
Repo: nerdCopter/bbl_parser PR: 2
File: LICENSE_COMMERCIAL:0-0
Timestamp: 2025-08-29T20:15:04.624Z
Learning: nerdCopter prefers clear positive indicators (✅) when describing fixes rather than using ❌ symbols which can be confusing when describing what was corrected in a positive context.
🧬 Code graph analysis (4)
src/parser/helpers.rs (1)
src/parser/header.rs (1)
val(194-194)
src/main.rs (5)
src/parser/helpers.rs (1)
sign_extend_14bit(58-64)src/parser/frame.rs (2)
parse_frame_data(437-597)skip_frame(632-671)src/parser/stream.rs (1)
new(16-23)src/types/frame.rs (2)
new(26-32)new(120-127)src/parser/event.rs (1)
parse_e_frame(38-174)
src/parser/stream.rs (1)
src/parser/helpers.rs (1)
sign_extend_14bit(58-64)
src/parser/decoder.rs (1)
src/parser/header.rs (1)
val(194-194)
🔇 Additional comments (18)
src/parser/helpers.rs (2)
1-64: LGTM! Well-structured sign extension helpers with good documentation.The implementations are correct:
- 8-bit and 16-bit use idiomatic Rust type casts for sign extension
- 2/4/6/24-bit use proper bitmask operations
- 14-bit sign-magnitude format is correctly documented and implemented
The unit tests provide good edge-case coverage for all functions.
66-129: Good test coverage for all sign extension functions.The tests appropriately verify boundary conditions and sign bit handling for each bit width. The 14-bit sign-magnitude tests correctly validate the unique behavior where
-0maps to0.src/parser/gps.rs (1)
122-133: LGTM! Correctly adapts to the newparse_frame_datasignature.Passing
falsefor the debug parameter is appropriate sinceparse_g_framealready has its own debug handling for GPS-specific output at lines 110-112.src/parser/mod.rs (1)
6-6: LGTM! Consistent module exposure pattern.The helpers module is correctly added and re-exported, following the same convention as the other parser submodules.
Also applies to: 15-15
src/parser/stream.rs (4)
1-4: LGTM! Clean import consolidation.All sign extension functions are now imported from the centralized helpers module, eliminating code duplication.
230-249: LGTM! Well-implemented variable-count 8SVB reader.The implementation correctly:
- Handles the single-value special case (line 235-236) without reading a header byte
- Caps iteration at
8.min(value_count)for safety (line 239)- Properly shifts the header for each iteration (line 245)
The callers in
frame.rsensurevalue_countnever exceeds the buffer size.
255-258: LGTM! Correctly delegates to the shared helper.The
read_neg_14bitfunction now usessign_extend_14bitfrom the helpers module while maintaining identical behavior.
261-333: LGTM! Tests correctly updated to use the external helper.The test import and assertions are consistent with the refactored code structure.
src/parser/frame.rs (3)
119-119: LGTM! parse_frame_data calls correctly pass the debug flag.Both I-frame (line 119) and P-frame (line 173) parsing now propagate the debug parameter to enable per-field debugging output when requested.
Also applies to: 173-173
535-572: LGTM! This correctly fixes the TAG8_8SVB encoding bug.The previous implementation always read 8 values regardless of how many consecutive fields used this encoding. The new logic:
- Counts consecutive fields with
ENCODING_TAG8_8SVB(lines 537-543)- Reads only the required number of values via
read_tag8_8svb_counted(line 545)- Applies predictors only for the actual group (lines 548-568)
- Advances by the correct count (line 570)
This aligns with the PR objective of fixing the "ENCODING_TAG8_8SVB bug (now counts consecutive fields correctly)".
458-469: LGTM! Predictor calls consistently updated.All predictor invocations now use
apply_predictor_with_debugwith the required parameters includingframe_namesfor field identification anddebugfor conditional output.Also applies to: 488-499, 518-529
src/main.rs (4)
16-24: LGTM! Clean import consolidation from crate library.The imports correctly bring in parser types, helpers, and encoding constants from the
bbl_parsercrate, eliminating the need for the localbbl_formatmodule. This aligns with the PR objective of making the crate the single source of parsing logic.
1750-1762: LGTM! Correct integration with crate's parse_frame_data.The call correctly passes all required parameters including the new
debugparameter, enabling debug output propagation through the crate's parsing logic.
2309-2312: LGTM! Encoding constants correctly imported from crate.The encoding constants (
ENCODING_SIGNED_VB,ENCODING_UNSIGNED_VB,ENCODING_NEG_14BIT,ENCODING_NULL) are properly imported from the crate and used consistently in frame parsing.
2299-2303: LGTM! Function signatures correctly use crate's BBLDataStream type.All frame parsing functions now consistently use
&mut BBLDataStreamfrom the crate library, ensuring unified stream handling.src/parser/decoder.rs (3)
55-80: LGTM! Good backward-compatible wrapper design.The original
apply_predictorfunction now delegates toapply_predictor_with_debugwith sensible defaults (skipped_frames: 0,field_names: &[],debug: false), maintaining backward compatibility for existing callers.
195-216: LGTM! Reasonable corruption prevention for PREDICT_VBATREF.The range check
(-1000..=4000).contains(&raw_value)provides reasonable bounds for valid delta values. The fallback to justvbatrefwhen corruption is detected prevents propagation of bad data.
131-153: LGTM! Predictor implementations handle edge cases properly.
PREDICT_STRAIGHT_LINEandPREDICT_AVERAGE_2correctly handle cases where previous frames are unavailable by falling back toraw_value.
Remove nonsensical string parsing of i32 values in PREDICT_MINMOTOR. Since sysconfig is HashMap<String, i32>, the values are already integers and don't need comma-separated string parsing. Addresses CodeRabbit review feedback.
There was a problem hiding this comment.
Actionable comments posted: 3
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
src/parser/frame.rs (1)
351-353: Operator precedence causes unintended behavior.Due to
&&having higher precedence than||, the condition evaluates as(debug && x) || y, meaning progress is printed every 100,000 frames regardless of thedebugflag.- if debug && stats.total_frames % 50000 == 0 || stats.total_frames % 100000 == 0 { + if debug && (stats.total_frames % 50000 == 0 || stats.total_frames % 100000 == 0) {
🧹 Nitpick comments (2)
src/parser/frame.rs (1)
608-610: Remove unused variable.The
_valuesarray is declared but never used—values are read directly intovalue.for field in &frame_def.fields { - let _values = [0i32; 1]; let value = match field.encoding {src/parser/decoder.rs (1)
14-19: Consider relocating threshold constant for clarity.
MAX_REASONABLE_VBAT_RAWis a corruption-detection threshold, not a predictor constant. Placing it betweenPREDICT_0andPREDICT_PREVIOUSbreaks the logical grouping of predictor constants.pub const PREDICT_0: u8 = 0; - -// Maximum reasonable raw vbatLatest value before considering it corrupted -const MAX_REASONABLE_VBAT_RAW: i32 = 1000; pub const PREDICT_PREVIOUS: u8 = 1; pub const PREDICT_STRAIGHT_LINE: u8 = 2; ... pub const PREDICT_MINMOTOR: u8 = 11; + +// Maximum reasonable raw vbatLatest value before considering it corrupted +const MAX_REASONABLE_VBAT_RAW: i32 = 1000;
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (5)
src/parser/decoder.rs(3 hunks)src/parser/frame.rs(8 hunks)src/parser/frame.rs.backup(1 hunks)src/parser/frame.rs.old(1 hunks)src/parser/frame.rs.original(1 hunks)
🧰 Additional context used
📓 Path-based instructions (2)
**/*.rs
📄 CodeRabbit inference engine (AGENTS.md)
**/*.rs: Do not remove or modify comments unless the related code is changed
Only add comments that explain code functionality; do not add AI instructional comments
Never embed or call external binaries from Rust code
Files:
src/parser/decoder.rssrc/parser/frame.rs
{src/**/*.rs,Cargo.*,README.md,OVERVIEW.md,.gitignore,.github/**}
📄 CodeRabbit inference engine (AGENTS.md)
Only commit changes to
src/**/*.rs,Cargo.*,README.md,OVERVIEW.md,.gitignore, and.github/**; never usegit add .orgit add -A
Files:
src/parser/decoder.rssrc/parser/frame.rs
🧠 Learnings (1)
📓 Common learnings
Learnt from: nerdCopter
Repo: nerdCopter/bbl_parser PR: 2
File: LICENSE_COMMERCIAL:1-4
Timestamp: 2025-08-29T19:52:05.099Z
Learning: nerdCopter prefers to avoid publishing personal information in license files for privacy and security reasons, as they are an individual maintainer rather than a company.
Learnt from: nerdCopter
Repo: nerdCopter/bbl_parser PR: 2
File: README.md:520-521
Timestamp: 2025-08-29T19:53:41.354Z
Learning: nerdCopter uses AGPL-3.0-or-later licensing for the bbl_parser project with a dual-licensing approach that includes a separate commercial license option.
Learnt from: nerdCopter
Repo: nerdCopter/bbl_parser PR: 2
File: CONTRIBUTING.md:9-14
Timestamp: 2025-08-21T20:25:45.741Z
Learning: nerdCopter prefers to keep CLA language general using "project maintainer" rather than specifying a legal entity name, as they are an individual maintainer without an associated company.
Learnt from: nerdCopter
Repo: nerdCopter/bbl_parser PR: 2
File: LICENSE_COMMERCIAL:0-0
Timestamp: 2025-08-29T20:15:04.624Z
Learning: nerdCopter prefers clear positive indicators (✅) when describing fixes rather than using ❌ symbols which can be confusing when describing what was corrected in a positive context.
🧬 Code graph analysis (1)
src/parser/frame.rs (1)
src/parser/decoder.rs (2)
apply_predictor_with_debug(88-239)decode_field_value(32-56)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
- GitHub Check: Test (windows-latest)
🔇 Additional comments (7)
src/parser/frame.rs (3)
1-10: Clean import organization.The imports properly separate the decoder utilities and explicitly import
apply_predictor_with_debug, making the dependency clear.
536-573: Correct 8SVB group counting implementation.The new logic properly counts consecutive
ENCODING_TAG8_8SVBfields before reading, ensuring that only the actual group size is processed. This fixes the bug mentioned in PR objectives where the encoding now correctly counts consecutive fields.
438-451: Debug propagation implemented correctly.The
parse_frame_datafunction signature now includesdebug: booland properly passes it to allapply_predictor_with_debugcalls, enabling consistent debug output across frame parsing as specified in the PR objectives.src/parser/decoder.rs (4)
58-83: Backward-compatible wrapper implementation.The existing
apply_predictorfunction now delegates toapply_predictor_with_debugwith sensible defaults, maintaining backward compatibility while enabling the enhanced functionality when needed.
103-132: Effective corruption prevention for vbatLatest.The logic correctly identifies when the previous frame's
vbatLatestvalue exceeds the reasonable threshold and falls back tovbatrefinstead of propagating corrupted data. This aligns with the "CRITICAL FIX" intent noted in comments.
204-225: Corruption prevention with raw value validation.The check validates that raw values for
vbatLatestfall within a reasonable range (-1000 to 4000). Values outside this range are treated as corrupted, and the function returns justvbatrefrather than propagating the bad delta.
227-235: Clean PREDICT_MINMOTOR implementation.The simplified lookup correctly tries
motorOutput[0]first, falls back tomotorOutput, and defaults to 48—matching the expected behavior without the previous erroneous string parsing.
- Extract magic number 1000 to MAX_REASONABLE_VBAT_RAW constant - Add debug logging for PREDICT_MOTOR_0 hardcoded fallback - Remove redundant function-scoped import (decoder::* glob already includes it) All tests pass, no clippy warnings.
57cc690 to
ed5bc26
Compare
There was a problem hiding this comment.
Actionable comments posted: 0
🧹 Nitpick comments (1)
src/parser/decoder.rs (1)
16-18: Consider relocating the constant for better organization.
MAX_REASONABLE_VBAT_RAWis a domain-specific threshold for corruption detection, placed between unrelated predictor constants (PREDICT_0andPREDICT_PREVIOUS). Consider grouping it with other domain-specific constants or near its usage in the vbatLatest corruption checks.// Predictor constants - directly from JavaScript reference pub const PREDICT_0: u8 = 0; - -// Maximum reasonable raw vbatLatest value before considering it corrupted -const MAX_REASONABLE_VBAT_RAW: i32 = 1000; pub const PREDICT_PREVIOUS: u8 = 1; ... pub const PREDICT_MINMOTOR: u8 = 11; + +// Maximum reasonable raw vbatLatest value before considering it corrupted +const MAX_REASONABLE_VBAT_RAW: i32 = 1000;
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (2)
src/parser/decoder.rs(3 hunks)src/parser/frame.rs(8 hunks)
🧰 Additional context used
📓 Path-based instructions (2)
**/*.rs
📄 CodeRabbit inference engine (AGENTS.md)
**/*.rs: Do not remove or modify comments unless the related code is changed
Only add comments that explain code functionality; do not add AI instructional comments
Never embed or call external binaries from Rust code
Files:
src/parser/decoder.rssrc/parser/frame.rs
{src/**/*.rs,Cargo.*,README.md,OVERVIEW.md,.gitignore,.github/**}
📄 CodeRabbit inference engine (AGENTS.md)
Only commit changes to
src/**/*.rs,Cargo.*,README.md,OVERVIEW.md,.gitignore, and.github/**; never usegit add .orgit add -A
Files:
src/parser/decoder.rssrc/parser/frame.rs
🧠 Learnings (1)
📓 Common learnings
Learnt from: nerdCopter
Repo: nerdCopter/bbl_parser PR: 2
File: LICENSE_COMMERCIAL:1-4
Timestamp: 2025-08-29T19:52:05.099Z
Learning: nerdCopter prefers to avoid publishing personal information in license files for privacy and security reasons, as they are an individual maintainer rather than a company.
Learnt from: nerdCopter
Repo: nerdCopter/bbl_parser PR: 2
File: README.md:520-521
Timestamp: 2025-08-29T19:53:41.354Z
Learning: nerdCopter uses AGPL-3.0-or-later licensing for the bbl_parser project with a dual-licensing approach that includes a separate commercial license option.
Learnt from: nerdCopter
Repo: nerdCopter/bbl_parser PR: 2
File: CONTRIBUTING.md:9-14
Timestamp: 2025-08-21T20:25:45.741Z
Learning: nerdCopter prefers to keep CLA language general using "project maintainer" rather than specifying a legal entity name, as they are an individual maintainer without an associated company.
Learnt from: nerdCopter
Repo: nerdCopter/bbl_parser PR: 2
File: LICENSE_COMMERCIAL:0-0
Timestamp: 2025-08-29T20:15:04.624Z
Learning: nerdCopter prefers clear positive indicators (✅) when describing fixes rather than using ❌ symbols which can be confusing when describing what was corrected in a positive context.
🧬 Code graph analysis (1)
src/parser/frame.rs (1)
src/parser/decoder.rs (2)
apply_predictor_with_debug(88-239)decode_field_value(32-56)
🔇 Additional comments (11)
src/parser/frame.rs (5)
1-4: LGTM!Import restructuring correctly brings in
apply_predictor_with_debugfrom the decoder module along with other decoder constants, aligning with the unified crate implementation.
112-124: LGTM!The
debugparameter is correctly propagated toparse_frame_datafor I-frame parsing, enabling debug instrumentation when requested.
440-451: LGTM!The updated
parse_frame_datasignature correctly includes thedebugparameter, enabling the enhanced debugging instrumentation to flow through the parsing pipeline.
536-573: Good fix for ENCODING_TAG8_8SVB handling.The group counting logic correctly determines the actual number of consecutive 8SVB-encoded fields rather than assuming a fixed count of 8. The bounds are properly constrained:
group_countis capped at 8 (max iterations)- The loop respects
frame_def.fields.len()to prevent out-of-bounds access- Increments
iby the actualgroup_countto advance correctlyThis addresses the bug mentioned in the PR objectives.
458-470: LGTM!The
apply_predictor_with_debugcall forPREDICT_INCcorrectly passes all required parameters including field names and debug flag for the enhanced corruption prevention and debug instrumentation.src/parser/decoder.rs (6)
58-83: LGTM!The wrapper preserves backward compatibility for existing callers while delegating to the enhanced implementation. Passing an empty
field_namesslice ensures legacy code paths don't inadvertently trigger field-name-based checks, maintaining consistent behavior.
85-99: LGTM!The function signature correctly exposes all parameters needed for the enhanced predictor logic. The clippy allow attribute appropriately acknowledges the complexity inherent in consolidating multiple concerns (prediction, debugging, corruption prevention).
103-132: LGTM!The corruption prevention for
vbatLatestis well-implemented:
- Uses safe pattern matching with
Option::mapfor field name lookup- Falls back to
vbatrefas a reasonable voltage baseline when corruption is detected- Debug logging provides visibility into when fixes are applied
163-185: Dynamic lookup with documented fallback.The dynamic
motor[0]lookup viafield_namesis preferred. The hardcoded fallback to index 39 is a reasonable safety net for edge cases where field names aren't available, and the debug logging makes it visible when the fallback is used.
204-225: LGTM!The corruption detection for
vbatLatestusing the range(-1000..=4000)is reasonable for voltage delta values. Returning justvbatrefon corruption provides a sensible default rather than propagating corrupted data.
227-235: Addressed: Logic error fix confirmed.The corrected implementation properly retrieves integer values from the HashMap without the erroneous string parsing logic from the previous version. The
or_elsechain and.copied()pattern is idiomatic Rust.
Move MAX_REASONABLE_VBAT_RAW constant to after predictor constants for better organization. Groups domain-specific constants together and improves code clarity by placing the constant near related corruption detection logic.
There was a problem hiding this comment.
Actionable comments posted: 2
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (1)
src/parser/decoder.rs(3 hunks)
🧰 Additional context used
📓 Path-based instructions (2)
**/*.rs
📄 CodeRabbit inference engine (AGENTS.md)
**/*.rs: Do not remove or modify comments unless the related code is changed
Only add comments that explain code functionality; do not add AI instructional comments
Never embed or call external binaries from Rust code
Files:
src/parser/decoder.rs
{src/**/*.rs,Cargo.*,README.md,OVERVIEW.md,.gitignore,.github/**}
📄 CodeRabbit inference engine (AGENTS.md)
Only commit changes to
src/**/*.rs,Cargo.*,README.md,OVERVIEW.md,.gitignore, and.github/**; never usegit add .orgit add -A
Files:
src/parser/decoder.rs
🧠 Learnings (1)
📓 Common learnings
Learnt from: nerdCopter
Repo: nerdCopter/bbl_parser PR: 2
File: LICENSE_COMMERCIAL:1-4
Timestamp: 2025-08-29T19:52:05.099Z
Learning: nerdCopter prefers to avoid publishing personal information in license files for privacy and security reasons, as they are an individual maintainer rather than a company.
Learnt from: nerdCopter
Repo: nerdCopter/bbl_parser PR: 2
File: README.md:520-521
Timestamp: 2025-08-29T19:53:41.354Z
Learning: nerdCopter uses AGPL-3.0-or-later licensing for the bbl_parser project with a dual-licensing approach that includes a separate commercial license option.
Learnt from: nerdCopter
Repo: nerdCopter/bbl_parser PR: 2
File: CONTRIBUTING.md:9-14
Timestamp: 2025-08-21T20:25:45.741Z
Learning: nerdCopter prefers to keep CLA language general using "project maintainer" rather than specifying a legal entity name, as they are an individual maintainer without an associated company.
Learnt from: nerdCopter
Repo: nerdCopter/bbl_parser PR: 2
File: LICENSE_COMMERCIAL:0-0
Timestamp: 2025-08-29T20:15:04.624Z
Learning: nerdCopter prefers clear positive indicators (✅) when describing fixes rather than using ❌ symbols which can be confusing when describing what was corrected in a positive context.
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (3)
- GitHub Check: Build Release Binaries (macos-latest, bbl_parser-macos, target/release/bbl_parser)
- GitHub Check: Build Release Binaries (windows-latest, bbl_parser-windows, target/release/bbl_parser.exe)
- GitHub Check: Build Release Binaries (ubuntu-latest, bbl_parser-linux, target/release/bbl_parser)
🔇 Additional comments (4)
src/parser/decoder.rs (4)
62-84: Good backward compatibility pattern.The wrapper maintains the original API signature while delegating to the enhanced version with sensible defaults. This allows existing callers to work unchanged while enabling new functionality.
104-133: Corruption prevention logic is well-structured.The vbatLatest corruption detection correctly identifies when previous values exceed reasonable thresholds and substitutes a sensible fallback. The bounds checking and optional debug logging are appropriate.
164-186: Dynamic field lookup with documented fallback is a good approach.The code attempts dynamic resolution first via field_names, then falls back to a documented hardcoded index. The debug logging makes the fallback path visible during troubleshooting.
228-236: Confirmed: Previous issue has been correctly fixed.The string parsing logic error has been properly addressed. The code now correctly retrieves integer values from the HashMap using
.or_else()and.copied()without attempting to parse i32 as comma-separated strings.
| // Domain-specific constants for corruption detection | ||
| // Maximum reasonable raw vbatLatest value before considering it corrupted | ||
| const MAX_REASONABLE_VBAT_RAW: i32 = 1000; |
There was a problem hiding this comment.
🧩 Analysis chain
🌐 Web query:
Betaflight blackbox vbat raw ADC value range typical
💡 Result:
Short answer: raw VBAT in Blackbox is the MCU ADC reading (12‑bit, 0–4095). With Betaflight's common 11:1 divider/vbat_scale default (110) and 3.3V ADC Vref, a 3S LiPo reads roughly 1,000–1,420 counts (≈9.0V → ~1,015; 12.6V → ~1,421). Blackbox logs that raw ADC value and provides vbatscale in the header for conversion. [1][2]
Sources:
- Betaflight CLI/vbat_scale (4095 = 12‑bit ADC, vbat_scale default 110). [1]
- Blackbox internals (blackbox logs raw VBAT and vbatscale header). [2]
🏁 Script executed:
# Search for vbat-related constants or references in the codebase
rg -i "vbat|voltage" --type rs -B 2 -A 2 | head -100Repository: nerdCopter/bbl_parser
Length of output: 91
🏁 Script executed:
# Check for any other corruption detection thresholds or value bounds
rg -i "max_reasonable|corruption|corrupt" --type rs -B 2 -A 2Repository: nerdCopter/bbl_parser
Length of output: 91
🏁 Script executed:
# Look at the context where MAX_REASONABLE_VBAT_RAW is used
rg "MAX_REASONABLE_VBAT_RAW" -B 3 -A 3 src/Repository: nerdCopter/bbl_parser
Length of output: 1078
Add explanation for the corruption detection threshold.
The MAX_REASONABLE_VBAT_RAW constant (1000) represents approximately 9.0V minimum for a 3S LiPo with Betaflight's default vbat_scale. This sits at the edge of typical valid voltage ranges (9.0–12.6V ≈ 1,000–1,420 counts), so the threshold choice should be documented to clarify whether readings below 9.0V genuinely indicate corruption or if the margin should be adjusted for operational safety.
🤖 Prompt for AI Agents
In src/parser/decoder.rs around lines 28 to 30, the MAX_REASONABLE_VBAT_RAW
constant (1000) lacks documentation about how it maps to real voltage and why it
was chosen; update the comment to explain the mapping from raw counts to volts
(using Betaflight's default vbat_scale), show the approximate voltage range
(e.g., ~9.0V for 1000 counts up to ~12.6V for ~1420 counts for a 3S LiPo), state
that 1000 was chosen as a conservative corruption threshold at the low edge, and
note whether this is intentionally strict or should be configurable so operators
can adjust the margin for operational safety.
| PREDICT_VBATREF => { | ||
| let vbatref = sysconfig.get("vbatref").copied().unwrap_or(4095); | ||
| Ok(value + vbatref) | ||
|
|
||
| // CRITICAL FIX: Check for corrupted raw values in vbatLatest | ||
| if !field_names.is_empty() | ||
| && field_names | ||
| .get(field_index) | ||
| .map(|name| name == "vbatLatest") | ||
| .unwrap_or(false) | ||
| && !(-1000..=4000).contains(&raw_value) | ||
| { | ||
| if debug { | ||
| eprintln!( | ||
| "DEBUG: Fixed corrupted vbatLatest raw_value {} replaced with 0", | ||
| raw_value | ||
| ); | ||
| } | ||
| return vbatref; | ||
| } | ||
|
|
||
| raw_value + vbatref | ||
| } |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
rg -n -C3 -i "vbat.*corrupt|corrupt.*vbat|vbat.*threshold|MAX_REASONABLE" --type rustRepository: nerdCopter/bbl_parser
Length of output: 2823
🏁 Script executed:
# Search for PREDICT_PREVIOUS implementation and related constants
rg -n "PREDICT_PREVIOUS" --type rust -A 15 -B 2Repository: nerdCopter/bbl_parser
Length of output: 2547
🏁 Script executed:
# Search for any magic numbers or constants related to vbat ranges
rg -n "4000|4095|1000" --type rust | head -20Repository: nerdCopter/bbl_parser
Length of output: 1704
🏁 Script executed:
# Check if there's documentation in the file about expected ranges
head -100 src/parser/decoder.rsRepository: nerdCopter/bbl_parser
Length of output: 3115
🏁 Script executed:
# Check the full context around PREDICT_VBATREF to see if there's explanation for the -1000..=4000 range
sed -n '200,230p' src/parser/decoder.rsRepository: nerdCopter/bbl_parser
Length of output: 1045
🏁 Script executed:
# Search for any other uses or definitions of the -1000..=4000 range or similar vbat thresholds
rg -n "\-1000|4000" src/parser/decoder.rs -B 2 -A 2Repository: nerdCopter/bbl_parser
Length of output: 273
Use the defined constant for consistency in PREDICT_VBATREF corruption threshold.
The range check !(-1000..=4000).contains(&raw_value) on line 214 should reference MAX_REASONABLE_VBAT_RAW constant (defined at line 30) for consistency with PREDICT_PREVIOUS (line 116), which uses the same constant. While the two predictors check different aspects (delta vs. absolute), the hard-coded range lacks justification. If the bounds differ intentionally, document why; otherwise, refactor to use the constant.
🤖 Prompt for AI Agents
In src/parser/decoder.rs around lines 205 to 226, the corruption check for
vbatLatest uses a hard-coded range !(-1000..=4000). Replace the magic numbers
with the MAX_REASONABLE_VBAT_RAW constant (defined at line 30) so the check
reads against that constant (and the negative of it for the lower bound if
intended), e.g., use !(-MAX_REASONABLE_VBAT_RAW..=MAX_REASONABLE_VBAT_RAW) or
otherwise use MAX_REASONABLE_VBAT_RAW for the upper bound and document why the
lower bound differs; update the debug message if needed to reflect the constant.
|
@coderabbitai, does this PR/branch fully resolve #11 ? is there anything missing for CRATE usage vs CLI usage? are all readers/parsers/processing now de-duplicated and both CRATE usable and CLI usable? if not, create new issue-ticket with deep analysis and iterative AI-instructional HOW-TO. I will merge this PR if you concur. |
|
Tip For best results, initiate chat on the files or code changes. 🧩 Analysis chain🏁 Script executed: Length of output: 2676 🏁 Script executed: Length of output: 4293 🏁 Script executed: Length of output: 5835 🏁 Script executed: Length of output: 9319 🏁 Script executed: Length of output: 3398 🏁 Script executed: Length of output: 1213 🏁 Script executed: Length of output: 8198 🏁 Script executed: Length of output: 8107 @nerdCopter, I've completed a comprehensive analysis of PR #12 against issue #11's objectives. Here's my assessment: ✅ Issue #11 is FULLY RESOLVED per stated objectivesAll three phases are complete: Phase 1 - Helper Functions: ✅ Complete
Phase 2 - parse_frame_data: ✅ Complete
Phase 3 - BBLDataStream & Cleanup: ✅ Complete
Metrics: Net -471 lines, 50 tests passing, CSV outputs identical ✅
|
YES, create new issue-ticket with deep analysis and detailed iterative AI-instructional HOW-TO. |
|
Tip For best results, initiate chat on the files or code changes. 🧩 Analysis chain🏁 Script executed: Length of output: 25988 🏁 Script executed: Length of output: 1412 🏁 Script executed: Length of output: 50 @nerdCopter, ✅ Follow-up issue created: #13 - Complete CLI-to-Crate Unification: Phase 4 (S/H/E Frame Parsers) What's in the IssueThe issue provides a comprehensive analysis and step-by-step HOW-TO for eliminating the remaining ~202 lines of duplication: 🔍 Detailed Analysis of 4 Areas
📋 Step-by-Step Migration PlanThe issue breaks down the work into 4 sub-phases (4a-4d) with:
✅ Benefits After Completion
🎯 Next StepsPR #12: ✅ Ready to merge — All Phase 1-3 objectives complete Issue #13: Work through phases 4a→4b→4c→4d sequentially (commit each phase separately for easy rollback) The issue is self-contained with all the analysis and instructions needed to complete the remaining unification work. Let me know if you need any clarification on the migration steps! 🧠 Learnings used |
…time. (#14) * refactor: move sign_extend helpers to crate library (Phase 1) - Create src/parser/helpers.rs with 7 sign_extend functions - Export helpers from parser/mod.rs - Remove bbl_format from lib.rs (will be CLI-only module) - Update stream.rs to use shared helpers - Update bbl_format.rs to use shared helpers - All tests pass, CSV output identical to master * refactor: unify parse_frame_data and BBLDataStream (Phase 2+3) - Add debug parameter to crate's parse_frame_data - Add apply_predictor_with_debug with field_names, skipped_frames support - Add vbatLatest corruption prevention logic to crate - Add read_tag8_8svb_counted method with proper group counting - Fix ENCODING_TAG8_8SVB handling to count consecutive fields - Update main.rs to import from bbl_parser crate: * BBLDataStream, parse_frame_data, FrameDefinition * sign_extend_14bit, encoding constants - Remove duplicate FieldDefinition/FrameDefinition from main.rs - Delete bbl_format.rs entirely (509 lines removed) - All CSV outputs verified identical to master branch * fix: simplify PREDICT_MINMOTOR logic (remove dead code) Remove nonsensical string parsing of i32 values in PREDICT_MINMOTOR. Since sysconfig is HashMap<String, i32>, the values are already integers and don't need comma-separated string parsing. Addresses CodeRabbit review feedback. * refactor: address CodeRabbit nitpick feedback - Extract magic number 1000 to MAX_REASONABLE_VBAT_RAW constant - Add debug logging for PREDICT_MOTOR_0 hardcoded fallback - Remove redundant function-scoped import (decoder::* glob already includes it) All tests pass, no clippy warnings. * refactor: reorganize MAX_REASONABLE_VBAT_RAW constant placement Move MAX_REASONABLE_VBAT_RAW constant to after predictor constants for better organization. Groups domain-specific constants together and improves code clarity by placing the constant near related corruption detection logic. * docs: add detailed documentation for MAX_REASONABLE_VBAT_RAW constant Explain voltage mapping from raw counts to volts using Betaflight's default vbat_scale (110). Document that 1000 raw counts ≈ 9.0V and 1420 ≈ 12.6V (fully charged 3S LiPo). Clarify threshold reasoning: 1000 was chosen as a conservative corruption detection threshold. Note that this is intentionally strict and may require configurability for operational safety in future versions. * refactor: replace magic numbers with MAX_REASONABLE_VBAT_RAW constant Replace hard-coded range -1000..=4000 with symmetric range using MAX_REASONABLE_VBAT_RAW constant (-MAX..=+MAX). Update debug message to include the constant value for clarity. Add inline comment explaining the symmetric range approach. * refactor: propagate debug parameter in GPS frame parsing Replace hardcoded false with debug parameter to enable consistent debug output when debug mode is enabled, matching the function's debug parameter signature. * refactor: elevate logging level for motor[0] fallback handling Replace DEBUG with WARNING prefix and enhance message clarity to highlight when motor[0] field is not found and fallback to hardcoded index 39 is used. Add comment noting this is frame-definition-dependent and may not work for all firmware versions. This increases visibility of a fragile fallback that should be investigated when encountered in production. * refactor: use stream.read_neg_14bit() for ENCODING_NEG_14BIT handling Replace manual reimplementation of ENCODING_NEG_14BIT handling (-(sign_extend_14bit(stream.read_unsigned_vb()? as u16))) with the existing stream.read_neg_14bit() method in parse_i_frame, parse_s_frame, and parse_h_frame functions. This provides consistency with decoder.rs, gps.rs, and frame.rs which already use the stream method. Remove unused sign_extend_14bit import. * feat: use 'Log start datetime' header for GPX timestamps Parse the 'H Log start datetime:' header from BBL files and use it to generate absolute timestamps in GPX exports, following betaflight's blackbox_decode approach of combining the log start datetime with frame timestamps. When the FC clock wasn't set (0000-01-01...), falls back to relative timestamps from Unix epoch. Changes: - Add log_start_datetime field to BBLHeader (types/header.rs) - Parse 'H Log start datetime:' header (parser/header.rs, main.rs) - Add datetime parsing and epoch conversion helpers - Update export_gpx_file to use parsed datetime for timestamps - Remove hardcoded 2025-03-26 date workaround * refactor: move GPX timestamp functions to conversion.rs library Move datetime/timestamp conversion functions from main.rs to conversion.rs library module so they are available for both CLI and crate users. Moved functions: - generate_gpx_timestamp() - main entry point for GPX timestamp generation - format_relative_timestamp() - fallback for relative time - parse_datetime_to_epoch() - ISO 8601 datetime parsing - ymd_to_days() / days_to_ymd() - date conversion helpers - is_leap_year() - leap year calculation main.rs now imports generate_gpx_timestamp from bbl_parser::conversion * fix: handle timezone offsets in ISO 8601 datetime parsing Previously the datetime parser ignored timezone offsets and treated all times as UTC, which would cause incorrect timestamps when logs include real-time data from GPS, RTC, or radio sources with non-UTC timezone configurations. Now properly parses and handles: - UTC indicator 'Z' - Positive offsets like '+02:00' (local time ahead of UTC) - Negative offsets like '-05:00' (local time behind UTC) Converts local time to UTC for consistent GPX timestamp generation. * docs: document datetime parsing format and add unit tests Add comprehensive documentation to parse_datetime_to_epoch explaining: - Exact Betaflight datetime format: YYYY-MM-DDTHH:MM:SS.mmm±HH:MM - Supported timezone formats (only colon-separated HH:MM) - Known limitations (no compact offsets like -0500, no region names) - Fractional seconds truncation behavior - Handling of 0000-01-01 placeholder datetime Add 13 unit tests covering: - UTC with Z suffix - Positive/negative timezone offsets - Zero offset equivalence - No timezone (treated as UTC) - Fractional seconds truncation - Betaflight default placeholder - Half-hour timezone offsets (e.g., +05:30) - Invalid format handling - Compact offset limitation documentation - GPX timestamp generation scenarios * refactor: extract shared timestamp formatting logic - Create epoch_seconds_to_iso8601() helper to eliminate duplication between generate_gpx_timestamp() and format_relative_timestamp() - Remove redundant format_relative_timestamp() function - Add defensive month assignment in days_to_ymd() for extra safety Addresses code quality nitpicks from review.
Closes #11
Changes
This PR eliminates significant code duplication between
src/bbl_format.rsand the crate library:Phase 1: Unified sign_extend helpers
src/parser/helpers.rswith 7 sign_extend functions (2-bit, 4-bit, 6-bit, 8-bit, 14-bit, 16-bit, 24-bit)parser::helpersmodulebbl_format.rsPhase 2+3: Unified frame parsing and stream
parse_frame_datawith debug parameter supportapply_predictor_with_debugwith field_names lookup and corruption prevention logicBBLDataStream- CLI now uses crate versionsrc/bbl_format.rsentirely (655 lines removed)main.rsto import from crate library:BBLDataStream,parse_frame_data,FrameDefinition, encoding constantsCode Reduction
Testing
Summary by CodeRabbit
Refactor
New Features
Bug Fixes
Tests
✏️ Tip: You can customize this high-level summary in your review settings.